#include <stdio.h>
#include <cassert>
#include <string.h>
#include <inttypes.h>

using namespace std;
#include "cbp3_def.h"
#include "cbp3_framework.h"

#include <map>

// local history based predictor, finite memory

#define PRED_SIZE 18 // 256K 2-bit counters = 64 KB cost

uint32_t *pc_to_history;
int8_t *history_to_counter;


void PredictorInit() {
    pc_to_history = new uint32_t[1 << PRED_SIZE];
    history_to_counter = new int8_t[1 << PRED_SIZE];
}

void PredictorReset() {
  for (int i = 0; i < (1 << PRED_SIZE); i ++) {
        pc_to_history[i] = 0;
	history_to_counter[i] = 0;
  }
}

void PredictorRunACycle() {
    // get info about what uops are processed at each pipeline stage
    const cbp3_cycle_activity_t *cycle_info = get_cycle_info();

    // make prediction at fetch stage
    for (int i = 0; i < cycle_info->num_fetch; i++) {
        uint32_t fe_ptr = cycle_info->fetch_q[i];
        const cbp3_uop_dynamic_t *uop = &fetch_entry(fe_ptr)->uop;

        if ( uop->type & IS_BR_CONDITIONAL ) {
	  //Get appropriate history:
	  uint32_t h = pc_to_history[(uop->pc & ((1 << PRED_SIZE) - 1))];

	  //Get prediction based on history:
	  bool pred = (history_to_counter[h & ((1 << PRED_SIZE) - 1)] >= 0);

          assert(report_pred(fe_ptr, false, pred, h & ((1 << PRED_SIZE) - 1) ));
        }


    }

    for (int i = 0; i < cycle_info->num_retire; i++) {
        uint32_t rob_ptr = cycle_info->retire_q[i];
        const cbp3_uop_dynamic_t *uop = &rob_entry(rob_ptr)->uop;

        if ( uop->type & IS_BR_CONDITIONAL) {

	  //Update predictor:
	  uint32_t h = pc_to_history[(uop->pc & ((1 << PRED_SIZE) - 1))];
	  bool t = uop->br_taken;
            if (t && history_to_counter[h & ((1 << PRED_SIZE) - 1)] < 1)
                history_to_counter[h & ((1 << PRED_SIZE) - 1)] ++;
            else if (!t && history_to_counter[h & ((1 << PRED_SIZE) - 1)] > -2)
                history_to_counter[h & ((1 << PRED_SIZE) - 1)] --;

        }
	//Update history:
	if (uop->type & IS_BR_CONDITIONAL) 
            pc_to_history[(uop->pc & ((1 << PRED_SIZE) - 1))] = 
	      (pc_to_history[(uop->pc & ((1 << PRED_SIZE) - 1))] << 1) | (uop->br_taken ? 1 : 0);
        else if (uop_is_branch(uop->type))
            pc_to_history[(uop->pc & ((1 << PRED_SIZE) - 1))] = 
	      (pc_to_history[(uop->pc & ((1 << PRED_SIZE) - 1))] << 1) | 1;
    }
}

void PredictorRunEnd() {
    // nothing
}

void PredictorExit() {
    delete [] history_to_counter;
    delete [] pc_to_history;
}
